/*
 * ________________________________________________________________________________________________________
 * Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
 *
 * This software, related documentation and any modifications thereto (collectively “Software”) is subject
 * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
 * and other intellectual property rights laws.
 *
 * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
 * and any use, reproduction, disclosure or distribution of the Software without an express license agreement
 * from InvenSense is strictly prohibited.
 *
 * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
 * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
 * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THE SOFTWARE.
 * ________________________________________________________________________________________________________
 */

#include "system_support.h"
#include "Icm20602Transport.h"
#include "Icm20602Defs.h"
#include "string.h"

#ifndef ICM20602_SPI_HANDLE
#error	"Please define ICM20602_SPI_HANDLE forward!" 
#endif

#define ICM20602_SPI_MAX_WAIT	10
#define ICM20602_SPI_MAX_LEN	32

/* forward declaration */
struct inv_icm20602;

#define READ_BIT_MASK			0x80
#define WRITE_BIT_MASK			0x7F

uint8_t icm20602_rx_buff[ICM20602_SPI_MAX_LEN];
uint8_t icm20602_tx_buff[ICM20602_SPI_MAX_LEN];

int inv_icm20602_read_reg(struct inv_icm20602 * s, uint8_t reg,	uint8_t * buf, uint32_t len)
{
	(void)s;
	int8_t ret = 0;
	uint8_t read_len = len + 1;
	
	memset(icm20602_tx_buff, 0, read_len);
	icm20602_tx_buff[0] = reg | READ_BIT_MASK; 

	if(buf == NULL){
		elog_error("icm20602_spi", "param error");
		return -1;
	}

	if(read_len >= ICM20602_SPI_MAX_LEN){
		elog_error("icm20602_spi", "param error");
		return -1;
	}
	
	HAL_GPIO_WritePin(ICM20602_NSS_GPIO, ICM20602_NSS_PIN, GPIO_PIN_RESET);
	
	HAL_SPI_TransmitReceive(ICM20602_SPI_HANDLE, icm20602_tx_buff, icm20602_rx_buff, read_len, ICM20602_SPI_MAX_WAIT);
	
	HAL_GPIO_WritePin(ICM20602_NSS_GPIO, ICM20602_NSS_PIN, GPIO_PIN_SET);

	memcpy(buf, &icm20602_rx_buff[1], len);

	return ret;
}

int inv_icm20602_write_reg(struct inv_icm20602 * s, uint8_t reg, const uint8_t * buf, uint32_t len)
{
	(void)s;
	int8_t ret = 0;
	uint8_t write_len = len + 1;

	if(buf == NULL){
		elog_error("icm20602_spi", "param error");
		return -1;
	}

	if(write_len >= ICM20602_SPI_MAX_LEN){
		elog_error("icm20602_spi", "param error");
		return -1;
	}
	
	icm20602_tx_buff[0] = reg & WRITE_BIT_MASK;
	
	memcpy(icm20602_tx_buff + 1, buf, len);
	
	HAL_GPIO_WritePin(ICM20602_NSS_GPIO, ICM20602_NSS_PIN, GPIO_PIN_RESET);

	ret += HAL_SPI_Transmit(ICM20602_SPI_HANDLE, icm20602_tx_buff, write_len, ICM20602_SPI_MAX_WAIT);
	
	HAL_GPIO_WritePin(ICM20602_NSS_GPIO, ICM20602_NSS_PIN, GPIO_PIN_SET);

	return ret;
}

// Only supported in ICM20690
int inv_icm20602_read_reg_ois(struct inv_icm20602 * s, uint8_t reg,	uint8_t * buf, uint32_t len)
{
	(void)s;
	int8_t ret = 0;

	return ret;
}

// Max size that can be read across I2C or SPI data lines
#define MAX_SERIAL_READ 16
// Max size that can be written across I2C or SPI data lines
#define MAX_SERIAL_WRITE 16

/* the following functions are used for configuring the secondary devices */

int inv_icm20602_mems_write_reg(struct inv_icm20602 * s, uint16_t reg, uint32_t length, const uint8_t *data)
{
    int result = 0;
	uint32_t bytesWrite = 0;
    const uint8_t regOnly = (uint8_t)(reg & 0x7F);

	while (bytesWrite<length)
	{
		int thisLen = INV_MIN(MAX_SERIAL_WRITE, length-bytesWrite);

		result |= inv_icm20602_write_reg(s, regOnly+bytesWrite, &data[bytesWrite], thisLen);

		if (result)
			return result;

		bytesWrite += thisLen;
	}

	return result;
}

int inv_icm20602_mems_write_reg_one(struct inv_icm20602 * s, uint16_t reg, uint8_t data)
{
	const uint8_t regOnly = (uint8_t)(reg & 0x7F);

	return inv_icm20602_write_reg(s, regOnly, &data, 1);
}

int inv_icm20602_mems_read_reg(struct inv_icm20602 * s, uint16_t reg, uint32_t length, uint8_t *data)
{
	int result = 0;
	uint32_t bytesRead = 0;
	const uint8_t regOnly = (uint8_t)(reg & 0x7F);

	while(bytesRead<length) {
		int thisLen = INV_MIN(MAX_SERIAL_READ, length-bytesRead);

		result |= inv_icm20602_read_reg(s, regOnly+bytesRead, &data[bytesRead], thisLen);

		if (result)
		return result;

		bytesRead += thisLen;
	}

	return result;
}

uint64_t inv_icm20602_get_time_us(void)
{
    return Get_SystemTimer_us();
}

void inv_icm20602_sleep_us(int us)
{
    Delay_us((uint32_t)us);
}

void inv_icm20602_sleep(int ms)
{
    Delay_ms((uint32_t)ms);
}
